from PySide2.QtGui import *
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from .scene import CGAIScene
from .node import Node
from .item import *
from .cgai import *
from .widgets.NodeNetwork import TabWindow
from .widgets.InputWidget import EditInput


class NodeGraph(QGraphicsView):
    """
    每创建一个节点，则将节点的所有节点加入到当前的全局节点map,分为
    all_point_items：所有连点
    all_line_items：所有线
    all_body_items: 所有主节点
    """
    def __init__(self,origin_rect=None):
        super(NodeGraph, self).__init__()

        self.UPDATE_MODE=CGAI.REALTIME_UPDATE

        self.mid_mouse_press = False  #中键按下开关
        self.left_mouse_press = False #左键按下开关
        self.dragging_line = False
        
        self.start_cur_pos = None
        self.end_cur_pos = None

        self.all_point_items = {}  #每成功创建一个节点后，该节点的所有连接节点加入该空间  {'pid':IPointItem,'pid':OPointItem}
        self.all_line_items = {}   #每成功创建一根连线后，该连线节点加入该空间  {'lid':Lineitem,'lid':Lineitem,...}
        self.connect_pitems = {}   #两个point item成对相连，以两者排序后的pid相连构成的键，值都为1。{'pid+pid':1,'pid+pid':2}

        self.all_body_items = {}
        self.nodeType_number_map = {}  #每类创建的节点递增尾缀，每类节点每创建一次，该对应值增加一类

        self.current_start_pitem = None  #当前正在拉线时的起始连点
        self.current_drag_line = None   #当前正在拉出的线


        self.selected_items=[]  #当前选中的item
        self.selected_line_items = []  #当前选中的LineItems
        self.hidde_selected_items=False

        self.tab_window = None  #节点搜索窗口
        self.edit_window = EditInput()  #双击更名窗口
        self.edit_window.edit_Signal.connect(self._rename)

        self.origin_sceneRect = origin_rect if origin_rect else QRectF(0,0,500,500)
        self.scene = CGAIScene(self.origin_sceneRect)
        self.setScene(self.scene)

        # self.parm_sceneRect = QRectF(250,250,100,100)#QGraphicsScene()
        # self.parm_scene = QGraphicsScene()
        # self.parm_scene.setSceneRect(self.parm_sceneRect)
        # self.invalidateScene()

        self.setRenderHints(QPainter.Antialiasing |
                            QPainter.HighQualityAntialiasing |
                            QPainter.TextAntialiasing |
                            QPainter.SmoothPixmapTransform
                            )
        self.setMouseTracking(1)
        self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)   #关闭水平滑动条
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)     #关闭垂直滑动条
        self.setTransformationAnchor(self.AnchorUnderMouse)
        self.setDragMode(self.RubberBandDrag)

        self.node = Node(self)   #初始化Node节点数据处理类

    def register_custom_nodes(self,node_data):
        """
        注册自定义节点
        :param node_data:
        :return:
        """
        self.node.r

    def _get_right_number(self,node_type):
        """
        用于创建节点时，更新该类节点的数量
        :param node_type:
        :return:
        """
        # number = 0
        # if node_type not in self.nodeType_number_map.keys():
        #     self.nodeType_number_map[node_type] = 0
        # else:
        #     number = self.nodeType_number_map[node_type] + 1
        number = self.nodeType_number_map.get(node_type,0)

        return number

    def _set_right_number(self,node_type):
        """
        创建节点成功后，更新节点类型尾缀数量
        :param node_type:
        :return:
        """
        if node_type not in self.nodeType_number_map.keys():
            self.nodeType_number_map[node_type] = 0
        else:
            self.nodeType_number_map[node_type] += 1

    def _rename(self,name):
        print('_rename start')
        if self.selected_items:
            i = self.selected_items[0]
            print(i.base_type)
            if i.base_type == 'BodyItem':
            # for i in self.selected_items:
            #     if i.base_type == 'BodyItem':
                i.setName(name)
                print('改名成功')

    def keyPressEvent(self,QKeyEvent):

        #创建节点
        if QKeyEvent.key() == Qt.Key_C:
            cur_pos = QCursor.pos()
            e_pos = self.mapFromGlobal(cur_pos)
            s_pos = self.mapToScene(e_pos)
            self.create_node(s_pos)
        # if QKeyEvent.key() == Qt.Key_D:
        #     self.scene.test_item1()

        if QKeyEvent.key() == Qt.Key_S:
            sitems=self.scene.selectedItems()
            print(len(sitems))
            for i in sitems:
                print(i)


        #隐藏与显示节点
        if QKeyEvent.key() == Qt.Key_X:  # 按X键 实现对选中节点的隐藏与显示
            self.hidde_selected_items = ~self.hidde_selected_items
            if self.hidde_selected_items:
                if self.selected_items:
                    for i in self.selected_items:
                        i.setVisible(False)
            else:
                if self.selected_items:
                    for i in self.selected_items:
                        i.setVisible(True)



        #聚焦，中心显示
        if QKeyEvent.key() == Qt.Key_F:
            bodyItem_pos = []
            if self.selected_items:
                for i in self.selected_items:
                    if i.base_type == 'BodyItem':
                        print(i.pos())
                        pos = i.pos()
                        # x = pos.x()
                        scene_pos = self.mapToScene(int(i.pos().x()),int(i.pos().y()))
                        bodyItem_pos.append(scene_pos)
                self.moveToCenter(bodyItem_pos[-1])


        #坐标测试
        if QKeyEvent.key() == Qt.Key_M:
            if self.selected_items:
                body_items = []
                for i in self.selected_items:
                    if i.base_type == 'BodyItem':
                        body_items.append(i)
                first_item = body_items[0]
                target_pos = first_item.pos()
                for b in body_items:
                    b.setPos(target_pos)


        #界面属性查询
        if QKeyEvent.key() == Qt.Key_F1:
            print('sceneRect:',self.sceneRect())  #永远不变是固定大小：sceneRect: PySide2.QtCore.QRectF(0.000000, 0.000000, 500.000000, 500.000000)

        if QKeyEvent.key() == Qt.Key_F2:
            print('transform:',self.transform())  #transform: PySide2.QtGui.QTransform(1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000)
            # self.setTransform(QTransform(1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000))

        if QKeyEvent.key() == Qt.Key_F3:
            if self.selected_items:

                # t = self.selected_items[0].transform()  #所求的是相对父空间的位移坐标，也就是0
                # dx = t.dx()
                # dy = t.dy()
                item = self.selected_items[0]
                pos = item.pos()
                # print(type(pos))
                # spos = self.mapToScene(QPoint(pos.x(),pos.y()))
                dx = pos.x()
                dy = pos.y()

                print('item.pos():',dx,dy)

                STD = QTransform(1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000)
                self.setTransform(STD)
                #因为变化位移操作是对scene的，缩放才是整个QGraphic,所有无论QGraphic的位移如何不影响场景的位置，要位移只能操作被改变的源头scene


                self.scene.setSceneRect(self.origin_sceneRect.x() + dx - self.scene.sceneRect().width()/2.0 + item.width/2.0,
                                        self.origin_sceneRect.y() + dy - self.scene.sceneRect().height()/2.0 + item.height/2.0,
                                        self.scene.sceneRect().width(), self.scene.sceneRect().height())
                self.scene.update()

                # new_t = STD*t
                # new_t = STD.translate(dx,dy)

                # print('new_t:',new_t)
                # self.setTransform(new_t)
                # self.setTransform(STD)
                # self.setTransform(new_t)
                # self.translate(dx,dy)


        #扩展场景视角：transform: PySide2.QtGui.QTransform(0.590490, 0.000000, 0.000000, 0.000000, 0.590490, 0.000000, 0.000000,
        #                                    0.000000, 1.000000)
        #状态切换



        if QKeyEvent.key() == Qt.Key_A:
            if self.selected_items:

                for i in self.selected_items:
                    if i.base_type == 'BodyItem':
                        print('切换完成')
                        i.set_status(CGAI.COMPLETED)

        if QKeyEvent.key() == Qt.Key_E:
            if self.selected_items:
                for i in self.selected_items:
                    if i.base_type == 'BodyItem':
                        print('切换完成')
                        i.set_status(CGAI.ERROR)

        if QKeyEvent.key() == Qt.Key_W:
            if self.selected_items:
                for i in self.selected_items:
                    if i.base_type == 'BodyItem':
                        print('切换完成')
                        i.set_status(CGAI.WAIT)

        #节点属性
        if QKeyEvent.key() == Qt.Key_U:
            if self.selected_items:
                print('selected_items:',self.selected_items)
                for i in self.selected_items:
                    print('base_type:',i.base_type)
                    if i.base_type == 'BodyItem':
                        input_items = i.get_up_items()
                        for u in input_items:
                            print(u.bid)

        if QKeyEvent.key() == Qt.Key_L:
            if self.selected_items:
                print('selected_items:',self.selected_items)
                for i in self.selected_items:
                    print('base_type:',i.base_type)
                    if i.base_type == 'BodyItem':
                        input_items = i.get_left_items()
                        for u in input_items:
                            print(u.bid)

        if QKeyEvent.key() == Qt.Key_I:
            if self.selected_items:
                print('selected_items:',self.selected_items)
                for i in self.selected_items:
                    print('base_type:',i.base_type)
                    if i.base_type == 'BodyItem':
                        print(i.bid)




        #打开节点窗口
        if QKeyEvent.key() == Qt.Key_Tab:
            print('打开节点窗口')
            cur_pos = QCursor.pos()
            # e_pos = self.mapFromGlobal(cur_pos)
            # s_pos = self.mapToScene(e_pos)
            default_data =['a','aa1','bb','c1','b2','cc']
            self.tab_window = TabWindow(default_data)
            self.tab_window.move(cur_pos)
            self.tab_window.show()

        #删除节点
        if QKeyEvent.key() == Qt.Key_Backspace or QKeyEvent.key() == Qt.Key_Delete:
            if self.selected_items:
                for i in self.selected_items:
                    self._remove_item(i)
                    # if isinstance(i,LineItem):
                    #     lid = i.lid
                    #     pids = i.get_pids()  #[start_pid,end_pid]
                    #     connect_pid = '_'.join(sorted(pids))
                    #     self.all_line_items.pop(lid)
                    #     self.connect_pitems.pop(connect_pid)
                    #     # self.scene.removeItem(i)
                    #     self._remove_item(i)

        super(NodeGraph, self).keyPressEvent(QKeyEvent)


    def _remove_line_item(self,line_item):
        """
        清除连线，需要清空：
            1.全局连线存储:self.all_line_items
            2.连线记录：self.connect_pitems
        :param line_item:
        :return:
        """
        lid = line_item.lid
        pids = line_item.get_pids()  # [start_pid
        # ,end_pid]
        line_item.break_points()
        connect_pid = '_'.join(sorted(pids))
        self.all_line_items.pop(lid)
        self.connect_pitems.pop(connect_pid)
        self.scene.removeItem(line_item)

    def _remove_item(self,item):


        if isinstance(item,BodyItem):
            pids = item.get_pids()
            for pid in pids:
                if pid in self.all_point_items.keys():
                    self.all_point_items.pop(pid)
            line_items = item.get_line_items()
            print('line_items:',line_items)
            for l in line_items:
                self._remove_line_item(l)
            self.scene.removeItem(item)

        elif isinstance(item,LineItem):
            self._remove_line_item(item)



    #
    # def keyReleaseEvent(self, QKeyEvent):
    #     if QKeyEvent.key() == Qt.Key_X:
    #         if self.selected_items:
    #             for i in self.select_items:
    #                 i.setVisible(True)
    #
    #
    #
    #     super(NodeGraph, self).keyPressEvent(QKeyEvent)

    def add_current_under_item(self,eventpos):
        """
        获取当前鼠标下的节点
        因为使用mousePressEvent不能立马获取当前鼠标下的节点，所以使用该函数来获取
        :param eventpos: 通过事件坐标来获取
        :return:
        """
        item=self.itemAt(eventpos)
        if item:
            if item.base_type == 'BodyItem' and item not in self.selected_items:
                self.selected_items.append(item)



    def mousePressEvent(self, QKeyEvent): #如果QGraphicsView也继承了super的mousePressEvent那么就会与scene中的item本身的mousePressEvent相冲突


        e_pos = QKeyEvent.pos()
        cur_pos = QCursor.pos()
        mapFromGlobal = self.mapFromGlobal(cur_pos)
        mapToScene = self.mapToScene(mapFromGlobal)
        print('e_pos:',e_pos)
        print('cur_pos:',cur_pos)
        print('mapFromGlobal:',mapFromGlobal)
        print('mapToScene:',mapToScene)



        #关闭节点搜索窗口
        if self.tab_window:
            self.tab_window.close()

        #关闭节点命名窗口
        if self.edit_window:
            # self.edit_window.close()
            self.edit_window.setHidden(True)

        # self.selected_items=self.scene.selectedItems()  # 点击鼠标后，里面获取一下当前选择的节点
        # print('鼠标点击后当前选择节点:',self.selected_items)
        self.add_current_under_item(QKeyEvent.pos())
        self.selected_line_items = self.get_selected_lineItems()  # 点击鼠标后，里面获取一下当前选择的连线
        print('鼠标点击后当前选择节点:',self.selected_items)
        print('鼠标点击后先获取',self.selected_line_items)
        if QKeyEvent.button() == Qt.MiddleButton:
            # self.setRubberBandSelectionMode()
            # print('中键按下')
            self.mid_mouse_press=~self.mid_mouse_press
            # print(QCursor.pos())
            cur_pos=QCursor.pos()
            pos = self.mapFromGlobal(cur_pos)
            # ts_pos=self.mapToScene(pos)
            ts_pos = self.mapToScene(pos)
            self.start_cur_pos = ts_pos

        else:
            super(NodeGraph, self).mousePressEvent(QKeyEvent)

        if QKeyEvent.button() == Qt.LeftButton:  # 按下鼠标左键时
            self.left_mouse_press = ~self.left_mouse_press

            # s_pos=self.mapToScene(e_pos)
            e_pos = QKeyEvent.pos()   #使用itemAt要用父节点的坐标，而不是自身的pos
            item=self.itemAt(e_pos.x(),e_pos.y())
            if item:
                print('点击的item为:',item)
                if isinstance(item,IPointItem) or isinstance(item,OPointItem): #既然在连接点头上，那么开始拉线
                    self.dragging_line = ~self.dragging_line
                    print('开始拉线')
                    self.draw_line_start(item.pid)  #当从连点开始时就创建连线
                # else:  #当是其他节点时，比如主节点


    def mouseMoveEvent(self, QMouseEvent):
        """
        实时移动鼠标
        :param QMouseEvent:
        :return:
        """

        if self.mid_mouse_press:   #当鼠标中键按下时
            cur_pos = QCursor.pos()
            relative_pos = self.mapFromGlobal(cur_pos)
            pos=self.mapToScene(relative_pos)

            self.end_cur_pos=pos    #实时获取鼠标在场景中的位置，并赋予给self.end_cur_pos
            self.update_scene(pos)  #刷新

        elif self.dragging_line:   #当仅鼠标左键按下拉线时
            #实时刷新连线item
            # print('实时刷新拉线')
            cur_pos=QCursor.pos()
            relative_pos=self.mapFromGlobal(cur_pos)
            pos=self.mapToScene(relative_pos)
            self.current_drag_line.set_endpos(pos)  #通过set_endpos实时绘制最终位置
            self.current_drag_line.update()

        if self.left_mouse_press:  #当左键按下时，
            # print('左键按下')
            # print(self.selected_line_items)
            if self.selected_line_items:  #当有选择的连线
                for line_item in self.selected_line_items:
                    line_item.update_pos()  #实时刷新


        super(NodeGraph, self).mouseMoveEvent(QMouseEvent)


    def mouseReleaseEvent(self, QMouseEvent):
        self.selected_items = self.scene.selectedItems()  #每次鼠标释放记录当前选中的items
        print('释放鼠标后，当前选择的items：',self.selected_items)
        self.selected_line_items= self.get_selected_lineItems()  #每次鼠标释放获取当前所选择关联到的连线LineItem
        if QMouseEvent.button()==Qt.MiddleButton:
            # self.scene.setSelectionArea()
            # print('中键释放')
            self.mid_mouse_press=~self.mid_mouse_press

        if QMouseEvent.button()==Qt.LeftButton:
            # print('左键释放')
            if self.dragging_line:  #释放左键时，当正在绘制连线时，检测当前鼠标有没有连到另外的连点
                self.dragging_line=~self.dragging_line
                e_pos=QMouseEvent.pos()
                # s_pos=self.mapToScene(e_pos)
                item= self.itemAt(e_pos.x(),e_pos.y())
                if isinstance(item,IPointItem) or isinstance(item,OPointItem):  #当连线其他连点后
                    print('连上其他连点了')
                    print('当前起始连点类型为:',self.current_start_pitem.itype)
                    print('当前鼠标下连点类型为：',item.itype)
                    start_pid = self.current_start_pitem.pid
                    current_pid = item.pid
                    key = '_'.join(sorted([start_pid,current_pid]))
                    # if key not in self.connect_pitems.keys():

                    if item.itype != self.current_start_pitem.itype and key not in self.connect_pitems.keys(): #仅当两连接点的类型不同时，才绘制生成完成的连线,还能保证不重复连线
                        print('两连点类型不同')
                        self.draw_line_end(item.pid)
                        self.connect_pitems[key] = 1
                    else:
                        print('两连点类型同类，无法创建连线')
                        self.scene.removeItem(self.current_drag_line)
                else:  #当没有连到其他连点时,清空已存在的拉线
                    self.scene.removeItem(self.current_drag_line)

            self.left_mouse_press = ~self.left_mouse_press

        super(NodeGraph, self).mouseReleaseEvent(QMouseEvent)

    def get_selected_lineItems(self):
        """
        获取当前选中的连线LineItems.
        获取方式：
            1.从已选中的所有item中获取base_type为'BodyItem'的主节点
            2.调用主节点的get_line_item获取关联到的LineItem
            
        :return: 
        """
        selected_line_items=[]
        if self.selected_items:
            for i in self.selected_items:
                if i.base_type=='BodyItem':
                    line_items=i.get_line_items()
                    for l in line_items:
                        if l not in selected_line_items:
                            selected_line_items.append(l)
        return selected_line_items

    def update_lineItmes_pos(self):
        """
        实时更新line_items的位置
        :param b_pos: 主节点坐标
        :return:
        """
        if self.selected_line_items:
            for i in self.line_items:
                i.update_pos()

    def draw_line_start(self, pid):
        """
        创建中间拉线并实时刷新
        :param pid: 连接节点id
        :return:
        """
        start_pitem = self.all_point_items.get(pid,None)
        print('draw_line_start:',start_pitem,pid)
        self.current_start_pitem = start_pitem
        self.current_drag_line = LineItem(self,start_pitem.pid)
        self.scene.addItem(self.current_drag_line)
        start_pos = self.current_drag_line.relative_parent_pos(start_pitem)
        self.current_drag_line.set_startpos(start_pos)

    def draw_line_end(self, pid):
        """
        结束连线，创建最终连线，并清空当前连线变量
        :param pid: 连接节点id
        :return:
        """

        end_pitem = self.all_point_items.get(pid,None)
        start_pitem = self.all_point_items.get(self.current_start_pitem.pid,None)
        total_line_item = LineItem(self,start_pitem.pid,end_pid=end_pitem.pid)
        total_line_item.bind_point_item(start_pitem,end_pitem)

        self.all_line_items[total_line_item.lid] = total_line_item
        self.scene.addItem(total_line_item)
        self.scene.removeItem(self.current_drag_line)
        self.current_start_pitem = None
        # print('total_line_item:',total_line_item.end_pos)



    def update_scene(self,scenePos,start_pos=None):

        disPointF = scenePos - self.start_cur_pos if not start_pos else scenePos - start_pos # prev_viewPos #缩放前后的坐标差值，即需要进行move的位移
        self.scene.setSceneRect(self.scene.sceneRect().x() - disPointF.x(), self.scene.sceneRect().y() - disPointF.y(),
                                self.scene.sceneRect().width(), self.scene.sceneRect().height())
        self.scene.update()
        # self.current_scene_pos = scenePos


    def wheelEvent(self, QWheelEvent):
        # if QApplication.keyboardModifiers()==Qt.ControlModifier:
        factor = QWheelEvent.angleDelta().y() / 120.0
        if QWheelEvent.angleDelta().y() / 120.0 > 0:
            factor = 1.2
            # print('滚轮角度大于0，往前拉，缩小视角，节点变大')
        else:
            # print('滚轮角度小于0，往后拉，拓展视角，节点变小')
            factor = 0.9
        current_width = self.transform().scale(factor, factor).mapRect(QRectF(0, 0, 1, 1)).width()
        if current_width < 0.12 or current_width > 5:
            return
        self.scale(factor, factor)

        super(NodeGraph,self).wheelEvent(QWheelEvent)

    def moveToCenter(self,pos):
        """
        节点居中显示
        :param pos:
        :return:
        """
        print('moveToCenter:',pos)
        # current_width = self.transform().scale(factor, factor).mapRect(QRectF(0, 0, 1, 1)).width()
        # self.scale(factor, factor)
        # self.update_scene(pos)


    def register_item(self,item):
        """
        注册该节点所有子节点，包括
        连接item
        主体item
        :param item:
        :return:
        """
        #注册连接item元素到全局存储
        ipitems = item.input_pitems
        opitems = item.output_pitems
        if ipitems:
            for ip in ipitems.values():
                self.all_point_items[ip.pid] = ip
        if opitems:
            for op in opitems.values():
                self.all_point_items[op.pid] = op

        #注册主体item
        self.all_body_items[item.bid] = item


    def create_node(self,pos):
        # item=CGAIItem()
        # item = self.node.createDefaultNode()
        # item = self.node.createNode('cgai',self,node_name='CGAI1')
        item = self.node.createNode('cgai',self)
        item.setPos(0,0) if not pos else item.setPos(pos-item.offset_pos)
        self.register_item(item)
        self.scene.addItem(item)



